﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.Serialization;
using MacomberMapSystem.Common.Internals;
using System.Data.Common;
using System.Drawing;
using System.IO;
using MacomberMapSystem.Common.Integration;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;
using MacomberMapSystem.Common.Integration.Blackstart;

namespace MacomberMapSystem.Common.Processing
{
    /// <summary>
    /// This class handles exporting Macomber Map model files for the map to use
    /// </summary>
    public class CIM_MMExporter : CIM_Processor
    {
        /// <summary>
        /// Initialize a new MM exporter
        /// </summary>
        /// <param name="xElem"></param>
        /// <param name="NamedParameters"></param>
        /// <param name="Repository"></param>
        public CIM_MMExporter(XmlElement xElem, Dictionary<String, Object> NamedParameters, MM_Repository Repository)
            : base(xElem, NamedParameters, Repository)
        {
        }


        /// <summary>Report the name of our process</summary>
        public override string ProcessName
        {
            get { return "Macomber Map Exporter"; }
        }

        /// <summary>
        /// Export our Macomber Map
        /// </summary>
        /// <param name="DbConn"></param>
        public override enumProcessState InitiateProcess(MM_Database_Connector DbConn)
        {
            //First, build our list of elements to be processed
            MM_Database_Model Model = NamedParameters["Model"] as MM_Database_Model;


            MM_Export_Elements ElementsToProcess = new MM_Export_Elements(Model);
            this.Total = ElementsToProcess.Count;

            


            //Now, write out our data
            WriteXmlFile(ProcessString(Path.Combine(TargetFolder, TargetFileName)), Model, DbConn, ElementsToProcess, Repository);

            //Write out our substation data file
            using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(Model.ModelFullPath, Model.NameWithoutExtension + "-SubstationCounties.xml"), new UTF8Encoding(false)))
            {
                xW.Formatting = Formatting.Indented;
                xW.WriteStartDocument();
                xW.WriteStartElement("ROWSET");
                MM_Substation FoundSub;
                foreach (CIM_Element Sub in Model.Substations.Values)
                    if (XmlConvert.ToBoolean(Sub["etx:Substation.forNetwork"]) && XmlConvert.ToBoolean(Sub["etx:Substation.forScada"]) && Repository.Db.Substations.TryGetValue(Sub.rdfID, out FoundSub))
                    {
                        xW.WriteStartElement("NMMS_SUBSTATION_COUNTY");
                        xW.WriteElementString("IDENTIFIER", Sub.rdfID.ToString());
                        xW.WriteElementString("STATION_ACRONYM", Sub.Name);
                        xW.WriteElementString("STATION_LONGNAME", Sub.LongName);
                        xW.WriteElementString("STATION_LATITUDE", FoundSub.Latitude.ToString());
                        xW.WriteElementString("STATION_LONGITUDE", FoundSub.Longitude.ToString());
                        xW.WriteElementString("STATION_COUNTY", FoundSub.Boundary.Name);
                        xW.WriteEndElement();
                    }


                xW.WriteEndElement();
                xW.WriteEndDocument();
            }

            //Save out our SVG file
            clsTomSawyer.LayoutAndSaveNetworkMap(Repository, Model, Path.Combine(TargetFolder, Path.GetFileNameWithoutExtension(TargetFileName) + ".svg"));

            return enumProcessState.Completed;
        }

        /// <summary>
        /// This class provides an enumerator for browsing through CIM elements in a memory-effective way
        /// </summary>
        public class MM_Export_Elements_Enumerator : IEnumerator<CIM_Element>
        {
            #region Variable declarations
            /// <summary>Our collection of current elements</summary>
            public CIM_Element[] CurrentElements = null;

            /// <summary>Our current index within the file</summary>
            public int CurrentIndex = 0;

            /// <summary>The current type being used</summary>
            public int CurrentType = 0;

            /// <summary>Our collection of types</summary>
            public List<String> TypeCollection = new List<string>();

            /// <summary>The model we're retrieveing el ements from</summary>
            public MM_Database_Model Model;            
            #endregion

            #region Initialization
            /// <summary>
            /// Intiialize our enumerator
            /// </summary>
            /// <param name="Model">Our network model</param>
            public MM_Export_Elements_Enumerator(MM_Database_Model Model)
            {
                this.Model = Model;
                TypeCollection.Add("cim:Company");
                TypeCollection.Add("cim:Contingency");
                TypeCollection.Add("etx:SettlementLoadZone");
                TypeCollection.Add("cim:SubGeographicalRegion");
                TypeCollection.Add("cim:Substation");
                TypeCollection.Add("cim:ACLineSegment");
                TypeCollection.Add("cim:SeriesCompensator");
                TypeCollection.Add("cim:BusbarSection");
                TypeCollection.Add("cim:SynchronousMachine");
                TypeCollection.Add("cim:CombinedCyclePlant");
                foreach (String ElemType in Model.Types)
                    if (ElemType.EndsWith("Load"))
                        TypeCollection.Add(ElemType);
                TypeCollection.Add("cim:PowerTransformer");
                TypeCollection.Add("cim:Disconnector");
                TypeCollection.Add("cim:Breaker");
                TypeCollection.Add("cim:GroundDisconnector");
                TypeCollection.Add("cim:ShuntCompensator");
                TypeCollection.Add("cim:StaticVarCompensator");
                TypeCollection.Add("etx:DCTie");
                TypeCollection.Add("etx:RemedialActionScheme");
                Reset();
            }
            #endregion

            /// <summary>
            /// Retrieve our current element
            /// </summary>
            public CIM_Element Current
            {
                get
                {
                    if (CurrentElements.Length == 0)
                    {
                        Console.WriteLine("Unable to locate any elements in " + TypeCollection[CurrentType] + "!");
                        return null;
                    }
                    else
                        return CurrentElements[CurrentIndex]; }
            }

            /// <summary>
            /// Return our current object
            /// </summary>
            object System.Collections.IEnumerator.Current
            {
                get { return CurrentElements[CurrentIndex]; }
            }

            /// <summary>
            /// Dispose our enumerator
            /// </summary>
            public void Dispose()
            {
                Reset();
            }

            /// <summary>
            /// Move to our next element, if we can
            /// </summary>
            /// <returns></returns>
            public bool MoveNext()
            {
                CurrentIndex++;
                if (CurrentElements == null && CurrentType == 0)
                    if (TypeCollection[CurrentType] == "cim:Contingency")
                        CurrentElements = Model.Traces.Values.ToArray();
                    else
                        CurrentElements = Model.ElementsOfType(TypeCollection[CurrentType]);                                        
                if (CurrentIndex == CurrentElements.Length)
                {
                    CurrentIndex = 0;
                    CurrentType++;
                    System.Windows.Forms.Application.DoEvents();
                    if (CurrentType == TypeCollection.Count)
                        return false;
                    else if (TypeCollection[CurrentType] == "cim:Contingency")
                        CurrentElements = Model.Traces.Values.ToArray();
                    else            
                        CurrentElements = Model.ElementsOfType(TypeCollection[CurrentType]);                    
                }
                return true;
            }

            /// <summary>
            /// Reset our pointer for elements
            /// </summary>
            public void Reset()
            {
                CurrentIndex = -1;
                CurrentType = 0;
                CurrentElements = null;
            }          
        }


        /// <summary>
        /// This class provides an interface to exportable elements for the MM processing
        /// </summary>
        public class MM_Export_Elements : IEnumerable<CIM_Element>
        {
            /// <summary>The CIM model associated with our elements</summary>
            private MM_Database_Model Model;


            /// <summary>
            /// Perform a quick back-of-the-envelope count of the total number of elements
            /// </summary>
            public float Count
            {
                get
                {
                    float Resp = 0;
                    Resp += Model.ElementsOfType("cim:Company").Length;
                    Resp += Model.ElementsOfType("cim:Substation").Length;
                    Resp += Model.ElementsOfType("cim:ACLineSegment").Length;
                    Resp += Model.ElementsOfType("cim:SeriesCompensator").Length;
                    Resp += Model.ElementsOfType("cim:BusbarSection").Length;
                    Resp += Model.ElementsOfType("cim:SynchronousMachine").Length;
                    Resp += Model.ElementsOfType("cim:CombinedCyclePlant").Length;
                    Resp += Model.ElementsOfType("etx:SettlementLoadZone").Length;
                    Resp += Model.ElementsOfType("cim:SubGeographicalRegion").Length;
                    foreach (String ElemType in Model.Types)
                        if (ElemType.EndsWith("Load"))
                            Resp += Model.ElementsOfType(ElemType).Length;
                    Resp += Model.ElementsOfType("cim:PowerTransformer").Length;
                    Resp += Model.ElementsOfType("cim:Disconnector").Length;
                    Resp += Model.ElementsOfType("cim:Breaker").Length;
                    Resp += Model.ElementsOfType("cim:GroundDisconnector").Length;
                    Resp += Model.ElementsOfType("cim:ShuntCompensator").Length;
                    Resp += Model.ElementsOfType("cim:StaticVarCompensator").Length;
                    Resp += Model.ElementsOfType("etx:DCTie").Length;
                    Resp += Model.Traces.Count;
                    return Resp;
                }
            }

            /// <summary>
            /// Initialize our export enumerable collection
            /// </summary>
            /// <param name="Model"></param>
            public MM_Export_Elements(MM_Database_Model Model)
            {
                this.Model = Model;
            }

            /// <summary>
            /// Return our enumerator
            /// </summary>
            /// <returns></returns>
            public IEnumerator<CIM_Element> GetEnumerator()
            {
                return new MM_Export_Elements_Enumerator(Model);
            }

            /// <summary>
            /// Return our enumerator
            /// </summary>
            /// <returns></returns>
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return new MM_Export_Elements_Enumerator(Model);               
            }
        }

      

        #region Exporting
        /// <summary>
        /// Write out our MM one-line of blackstart elements
        /// </summary>
        /// <param name="Model"></param>
        /// <param name="TargetFile"></param>
        private void WriteBlackstartElements(MM_Database_Model Model, string TargetFile)
        {
            XmlDocument OutBlackstart = new XmlDocument();
            OutBlackstart.AppendChild(OutBlackstart.CreateElement("One_Line"));
            OutBlackstart.DocumentElement.Attributes.Append(OutBlackstart.CreateAttribute("rdfID")).Value = "{F2ACC59B-78BE-4DED-909B-2DBDB9BE5FAE}";
            OutBlackstart.DocumentElement.Attributes.Append(OutBlackstart.CreateAttribute("BaseElement.ElemType")).Value = "Blackstart_Corridor";
            OutBlackstart.DocumentElement.Attributes.Append(OutBlackstart.CreateAttribute("BaseElement.Name")).Value = "Blackstart Corridor Elements";
            OutBlackstart.DocumentElement.Attributes.Append(OutBlackstart.CreateAttribute("BaseElement.LongName")).Value = "Blackstart Corridor Elements";
            XmlElement xElements = OutBlackstart.CreateElement("Elements");
            OutBlackstart.DocumentElement.AppendChild(xElements);
            OutBlackstart.DocumentElement.AppendChild(OutBlackstart.CreateElement("Nodes"));
            OutBlackstart.DocumentElement.AppendChild(OutBlackstart.CreateElement("Unlinked_Elements"));


            //Build our list of blackstart elements
            CIM_RdfID rdfID;
            CIM_Element AssocElem;

            Dictionary<CIM_RdfID, CIM_Element> BlackstartElements = new Dictionary<CIM_RdfID, CIM_Element>();
            foreach (MM_Blackstart_Corridor Corridor in Repository.Db.Blackstart_Corridors)
                foreach (MM_Blackstart_Corridor_Target Target in Corridor.CorridorTargets)
                    foreach (MM_Blackstart_Corridor_Element[] Elems in new MM_Blackstart_Corridor_Element[][] { Target.Primary, Target.Secondary })
                        if (Elems != null)
                            foreach (MM_Blackstart_Corridor_Element Elem in Elems)
                                if (Elem.xConfig.HasAttribute("rdfID") && !BlackstartElements.ContainsKey(rdfID = new CIM_RdfID(Elem.xConfig.Attributes["rdfID"].Value)) && (AssocElem = Model.FromRdfID(rdfID)) != null && AssocElem.ElemType == "Breaker")
                                    BlackstartElements.Add(rdfID, AssocElem);


            int StartHeight = 20;
            float CurrentElem = 0;
            foreach (CIM_Element Elem in BlackstartElements.Values)
                using (MM_OneLine_Element MMElem = new MM_OneLine_Element(Elem, Model.Traces[0], OutBlackstart))
                {
                    CurrentElem++;
                    ProcessLink.Title = "Writing blackstart elements: " + (CurrentElem / (float)BlackstartElements.Count).ToString("0.0%");
                    xElements.AppendChild(MMElem.Definition);
                    MMElem.Descriptor = new MM_OneLine_Element(Elem, MMElem.Definition["Descriptor"]);
                    MMElem.Descriptor.Left = (MMElem.Left = 20) + MMElem.Width + 20;
                    MMElem.Top = MMElem.Descriptor.Top = StartHeight;
                    StartHeight += MMElem.Height + MM_Serializable<Rectangle>.ConvertObject(MMElem.Descriptor.Definition.Attributes["LabelBounds"].Value, null).Height + 10;
                    MMElem.Definition.Attributes["Bounds"].Value = MM_Serializable.GetStringForXml(MMElem.Bounds);
                    MMElem.Definition["Descriptor"].Attributes["Bounds"].Value = MM_Serializable.GetStringForXml(MMElem.Descriptor.Bounds);
                }

            MM_Database_OneLine OutOl = new MM_Database_OneLine(OutBlackstart, new CIM_RdfID("{F2ACC59B-78BE-4DED-909B-2DBDB9BE5FAE}"), 0, "Blackstart Corridor Elements", Model);
            OutOl.WriteOneLine(Path.Combine(Path.GetDirectoryName(TargetFile), "Blackstart_Corridor_Elements.MM_OneLine"), Model);
            //MacomberMapSystem.Common.Exports.MM_Oneline_Export OLExport = new MacomberMapSystem.Common.Exports.MM_Oneline_Export(OutOl, Model);
            //MacomberMapSystem.Common.Exports.MM_Image_Exporter.ExportImage(OutOl, Model, Path.Combine(Path.GetDirectoryName(TargetFile), "Blackstart_Corridor_Elements.png"), System.Drawing.Imaging.ImageFormat.Png, OLExport);
        }

        /// <summary>
        /// Write out our target XML file
        /// </summary>
        /// <param name="TargetFile"></param>
        /// <param name="Model"></param>
        /// <param name="DbConn"></param>
        /// <param name="ElementsToProcess"></param>
        /// <param name="Repository"></param>
        private void WriteXmlFile(String TargetFile, MM_Database_Model Model, MM_Database_Connector DbConn, MM_Export_Elements ElementsToProcess, MM_Repository Repository)
        {

            //Start by writing up a pseudo-oneline for our blackstart corridor elements
            this.ProcessLink.Title = "Writing blackstart elements file";
            WriteBlackstartElements(Model, TargetFile);

            //Track our substation coordinates
            Dictionary<PointF, MM_Substation> SubstationCoordinates = new Dictionary<PointF, MM_Substation>();

            Current = 0;

            XmlWriterSettings OutSettings = new XmlWriterSettings();
            OutSettings.Encoding = new UTF8Encoding(false);
            OutSettings.Indent = true;

            this.ProcessLink.Title = "Writing XML file";

            using (XmlTextWriter xW = new XmlTextWriter(TargetFile, new UTF8Encoding(false)))
            {
                xW.Formatting = Formatting.Indented;
                xW.WriteStartDocument(true);
                xW.WriteStartElement("NetworkModel");
                xW.WriteAttributeString("SaveTime", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified));
                xW.WriteAttributeString("Model", Path.GetFileNameWithoutExtension(Model.Name));

                //Now, print every county
                foreach (MM_Boundary Boundary in Repository.Db.Boundaries.Values)
                    WriteBoundary(Boundary, xW);
                WriteBoundary(Repository.Db.StateBoundary, xW);




                //Now, go element by element, exporting data
                MM_Export_Elements_Enumerator tempEnum = new MM_Export_Elements_Enumerator(Model);

                foreach (CIM_Element Elem in ElementsToProcess)
                    try
                    {
                        Current++;
                        if (Elem.TypeName == "etx:RemedialActionScheme")
                        {
                            xW.WriteStartElement("RemedialActionScheme");
                            xW.WriteAttributeString("ElemType", XmlConvert.ToBoolean(Elem["IsRAP"]) ? "RAP" : "SPS");
                            if (!String.IsNullOrEmpty(Elem["etx:RemedialActionScheme.Contigency"]))
                                xW.WriteAttributeString("Contingencies", Elem.CIM.FromRdfID(Elem["etx:RemedialActionScheme.Contigency"].Substring(1)).Name);
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("Description", MM_Type_Converter.TitleCase(Elem.LongName));

                            StringBuilder sB = new StringBuilder();
                            foreach (CIM_Element Elem2 in Elem.Elements)
                                sB.Append((sB.Length == 0 ? "" : ",") + Elem2.TEID);
                            xW.WriteAttributeString("Elements", sB.ToString());

                            //Now, go through and if we don't have our element in the type collection, write it out.
                            foreach (CIM_Element Elem2 in Elem.Elements)
                                if (!tempEnum.TypeCollection.Contains(Elem2.TypeName))
                                {
                                    xW.WriteStartElement(Elem2.ElemType);
                                    xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem2.Name));
                                    xW.WriteAttributeString("TEID", Elem2.TEID);
                                    if (Elem.Owner != null)
                                        xW.WriteAttributeString("Owner", Elem2.Owner.TEID);
                                    else
                                        xW.WriteAttributeString("Owner", Elem2.Substation.Owner.TEID);
                                    xW.WriteAttributeString("Operator", Elem2.Operator.TEID);
                                    xW.WriteAttributeString("ElemType", Elem2.ElemType);
                                    xW.WriteAttributeString("KVLevel", GetKV(Elem2.VoltageLevel));
                                    xW.WriteAttributeString("Substation", Elem2.Substation.TEID);
                                    WriteTraces(xW, Elem2);
                                    xW.WriteEndElement();
                                }
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:Company")
                        {
                            xW.WriteStartElement("Company");
                            String PhoneNumber = Elem["etx:Company.PrimaryPhone"];
                            long TryParse, TryParse2;
                            if (long.TryParse(PhoneNumber, out TryParse))
                                if (PhoneNumber.Length == 10)
                                    xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000"));
                                else
                                    xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000 x\\." + new string('0', PhoneNumber.Trim().Length - 10)));
                            else if (PhoneNumber.IndexOf("x", StringComparison.CurrentCultureIgnoreCase) != -1 && long.TryParse(PhoneNumber.Split('x', 'X')[0].Trim(), out TryParse) && long.TryParse(PhoneNumber.Split('x', 'X')[1].Trim(), out TryParse2))
                                xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000") + " x." + TryParse2.ToString());
                            else if (PhoneNumber == "(999) 999-9999 x____")
                                xW.WriteAttributeString("PrimaryPhone", " -Unknown- ");
                            else
                                xW.WriteAttributeString("PrimaryPhone", PhoneNumber);

                            if (long.TryParse(Elem["etx:Company.DUNS"], out TryParse))
                                xW.WriteAttributeString("DUNS", TryParse.ToString("#,##0"));
                            else
                                xW.WriteAttributeString("DUNS", Elem["etx:Company.DUNS"]);
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("ElemType", "Company");
                            xW.WriteAttributeString("Alias", Elem["cim:IdentifiedObject.aliasName"]);
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:Substation" && XmlConvert.ToBoolean(Elem["etx:Substation.forNetwork"]) && XmlConvert.ToBoolean(Elem["etx:Substation.forScada"]))
                        {
                            xW.WriteStartElement("Substation");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);


                            bool HasSynchroscope = false, HasSynchroCheckRelay = false;
                            foreach (CIM_Element Breaker in Elem.Elements)
                                if (Breaker.TypeName == "cim:Breaker")
                                {
                                    CIM_Element Operator = Breaker.FindOperator();
                                    if (Operator != null && (Repository.Db.Synchrocheck_Relays.ContainsKey(Breaker.rdfID) || Repository.Db.Synchrocheck_Relays.ContainsKey(Elem.rdfID) || Repository.Db.Synchrocheck_Relays.ContainsKey(Operator.rdfID)))
                                        HasSynchroCheckRelay = true;
                                    if (Operator != null && (Repository.Db.Synchroscopes.ContainsKey(Breaker.rdfID) || Repository.Db.Synchroscopes.ContainsKey(Elem.rdfID) || Repository.Db.Synchroscopes.ContainsKey(Operator.rdfID)))
                                        HasSynchroscope = true;
                                }
                            xW.WriteAttributeString("HasSynchroscope", HasSynchroscope.ToString());
                            xW.WriteAttributeString("HasSynchrocheck", HasSynchroCheckRelay.ToString());


                            String LongName = MM_Type_Converter.TitleCase(Elem["cim:IdentifiedObject.description"]);
                            if (String.IsNullOrEmpty(LongName))
                                LongName = MM_Type_Converter.TitleCase(Elem.Name);

                            MM_Substation FoundSub;

                            //Find our substation, or create a new one
                            if (!Repository.Db.Substations.TryGetValue(Elem.rdfID, out FoundSub))
                            {
                                FoundSub = new MM_Substation(Elem, Repository);
                                FoundSub.LatLong = PointD.Empty;
                                FoundSub.Boundary = Repository.Db.StateBoundary;
                                Repository.Db.Substations.Add(FoundSub.rdfID, FoundSub);
                                FoundSub.WriteRdb(Repository);
                                ProcessLog.AppendLine("Created new substation " + FoundSub.Name + " (" + FoundSub.LongName + ")");
                            }

                            //Validate our substation coordinates. If invalid, update it.
                            if (double.IsNaN(FoundSub.Latitude) || FoundSub.Latitude == 0.0)
                                using (StringWriter sW2 = new StringWriter(ProcessLog))
                                    CIM_Model_Maintenance.UpdateCoordinates(FoundSub, Elem, 0, sW2, Repository);

                            MM_Substation MismatchedSub;
                            if (SubstationCoordinates.TryGetValue(FoundSub.LatLong.ToPointF(), out MismatchedSub))
                                ProcessLog.AppendLine("Coordinate collision detected between substations " + MismatchedSub.Name + " (" + MismatchedSub.LongName + ") and " + FoundSub.Name + " (" + FoundSub.LongName + ")\r\n\r\n");
                            else
                                SubstationCoordinates.Add(FoundSub.LatLong.ToPointF(), FoundSub);

                            xW.WriteAttributeString("LongName", LongName);
                            xW.WriteAttributeString("Longitude", FoundSub.LatLong.X.ToString());
                            xW.WriteAttributeString("Latitude", FoundSub.LatLong.Y.ToString());
                            xW.WriteAttributeString("LatLong", FoundSub.LatLong.X.ToString() + "," + FoundSub.LatLong.Y.ToString());
                            xW.WriteAttributeString("PUNElement", Model.PUNStations.Contains(Elem).ToString());

                            Dictionary<String, List<CIM_Element>> ElemTally = new Dictionary<string, List<CIM_Element>>();
                            List<String> KVLevels = new List<string>();
                            List<String> BusNumbers = new List<string>();

                            foreach (CIM_Element VoltageLevel in Elem.Links_Elements)
                            {
                                if (VoltageLevel.TypeName == "cim:VoltageLevel")
                                {
                                    CIM_Element Bus = VoltageLevel.ParseSingle("<etx:ConnectivityNodeGroup",null);
                                    if (Bus != null)
                                        BusNumbers.Add(Bus["etx:ConnectivityNodeGroup.PSSEBusNumber"]);
                                           
                                    string KV = GetKV(VoltageLevel);
                                    if (!KVLevels.Contains(KV))
                                        KVLevels.Add(KV);
                                    foreach (CIM_Element Equip in VoltageLevel.Links_Elements)
                                        if (Equip.TypeName == "cim:TransformerWinding")
                                            AddElement(ElemTally, "TransformerWinding", Equip);
                                        else if (Equip.TypeName.EndsWith("Load"))
                                            AddElement(ElemTally, "Load", Equip);
                                        else if (Equip.TypeName == "cim:SynchronousMachine")
                                            AddElement(ElemTally, "Unit", Equip);
                                        else if (Equip.TypeName == "cim:BusbarSection")
                                            AddElement(ElemTally, "BusbarSection", Equip);
                                        else if (Equip.TypeName == "cim:ShuntCompensator")
                                            AddElement(ElemTally, "ShuntCompensator", Equip);
                                        else if (Equip.TypeName == "cim:StaticVarCompensator")
                                            AddElement(ElemTally, "StaticVarCompensator", Equip);
                                }
                                else if (VoltageLevel.TypeName == "cim:PowerTransformer")
                                    AddElement(ElemTally, "Transformer", VoltageLevel);
                            }

                            xW.WriteAttributeString("PSSEBusNumbers", string.Join(",", BusNumbers.ToArray()));
                            xW.WriteAttributeString("KVLevels", string.Join(",", KVLevels.ToArray()));
                            WriteTraces(xW, Elem);
                            //TODO: Write county and load zone here
                            if (FoundSub.Boundary == null)
                                xW.WriteAttributeString("County", "STATE");
                            else
                                xW.WriteAttributeString("County", FoundSub.Boundary.Name);


                            xW.WriteAttributeString("ElemTypes", GetElemTypes(ElemTally));
                            foreach (KeyValuePair<String, List<CIM_Element>> kvp in ElemTally)
                                xW.WriteAttributeString(kvp.Key + "s", GetTEIDs(kvp.Value));

                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "Substation");
                            //xW.WriteAttributeString("WeatherZone", Elem.ParseSingle("cim:SubGeographicalRegion", null).Name);
                            //xW.WriteAttributeString("LoadZone", Elem.ParseSingle("etx:SettlementLoadZone", null).Name);
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:ACLineSegment" || Elem.TypeName == "cim:SeriesCompensator")
                        {
                            xW.WriteStartElement("Line");
                            xW.WriteAttributeString("IsZBR", (String.IsNullOrEmpty(Elem["cim:Conductor.x"]) || Elem["cim:Conductor.x"] == "0") && (String.IsNullOrEmpty(Elem["cim:Conductor.r"]) || Elem["cim:Conductor.r"] == "0") ? "true" : "false");

                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            CIM_Element Sub1 = Elem.ACSegmentStations[0];
                            CIM_Element Sub2 = Elem.ACSegmentStations[1];
                            CIM_Element LineParent = Elem.ParseSingle("cim:Line", null);
                            MM_Line FoundLine;
                            Repository.Db.Lines.TryGetValue(Elem.rdfID, out FoundLine);

                            xW.WriteAttributeString("Name", Elem.LineName);
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("Substation1", Sub1.TEID);
                            xW.WriteAttributeString("Substation2", Sub2.TEID);
                            CIM_Element[] LineConnectivityNodes = Elem.ACSegmentNodes;

                            xW.WriteAttributeString("NodeTEIDs", LineConnectivityNodes[0].TEID + "," + LineConnectivityNodes[1].TEID);
                            xW.WriteAttributeString("Sub1Name", Sub1.Name);
                            xW.WriteAttributeString("Sub2Name", Sub2.Name);

                            CIM_Element NormLimit = null, TwoHrLimit = null, FifteenMinLimit = null;
                            foreach (CIM_Element LimitElem in Elem.Parse("<Analog<AnalogLimitSet<AnalogLimit", null))
                                if (LimitElem["etx:AnalogLimit.limitType"].EndsWith("fifteenminuteRating"))
                                    FifteenMinLimit = LimitElem;
                                else if (LimitElem["etx:AnalogLimit.limitType"].EndsWith("twoHourRating"))
                                    TwoHrLimit = LimitElem;
                                else if (LimitElem["etx:AnalogLimit.limitType"].EndsWith("normalRating"))
                                    NormLimit = LimitElem;

                            if (NormLimit != null)
                                xW.WriteAttributeString("NormalLimit", NormLimit["cim:AnalogLimit.value"]);
                            if (TwoHrLimit != null)
                                xW.WriteAttributeString("EmergencyLimit", TwoHrLimit["cim:AnalogLimit.value"]);
                            if (FifteenMinLimit != null)
                                xW.WriteAttributeString("LoadshedLimit", FifteenMinLimit["cim:AnalogLimit.value"]);

                            WriteTraces(xW, Elem);
                            xW.WriteAttributeString("ConnectedStations", Sub1.TEID + "," + Sub2.TEID);
                            xW.WriteAttributeString("LineTEID", Elem.ParseSingle("cim:Line", null).TEID);

                            if (FoundLine != null)
                            {
                                StringBuilder sB = new StringBuilder();
                                foreach (PointD LatLng in FoundLine.Coordinates)
                                    sB.Append((sB.Length == 0 ? "" : ",") + LatLng.X.ToString() + "," + LatLng.Y.ToString());
                                xW.WriteAttributeString("Coordinates", sB.ToString());

                                sB = new StringBuilder();
                                foreach (MM_Boundary Bound in FoundLine.ThroughBoundaries)
                                    sB.Append((sB.Length == 0 ? "" : ",") + Bound.ID);
                                //xW.WriteAttributeString("Counties", sB.ToString());
                            }
                            else
                            {
                                PointD Sub1C = Repository.Db.Substations[Sub1.rdfID].LatLong;
                                PointD Sub2C = Repository.Db.Substations[Sub2.rdfID].LatLong;
                                xW.WriteAttributeString("Coordinates", Sub1C.X.ToString() + "," + Sub1C.Y.ToString() + "," + Sub2C.X.ToString() + "," + Sub2C.Y.ToString());
                            }

                            xW.WriteAttributeString("IsSeriesCompensator", (Elem.TypeName == "cim:SeriesCompensator" ? "True" : "False"));
                            xW.WriteAttributeString("IsMultipleSegment", Elem.ParseSingle("cim:Line", null).InLinks.Length > 1 ? "True" : "False");
                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            else if (LineParent.Owner != null)
                                xW.WriteAttributeString("Owner", LineParent.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "Line");
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:BusbarSection")
                        {
                            CIM_Element Node = Elem.ParseSingle("cim:Terminal>cim:ConnectivityNode", null);
                            xW.WriteStartElement("BusbarSection");
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "BusbarSection");
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteAttributeString("Substation", Node.Substation.TEID);
                            WriteTraces(xW, Elem);

                            xW.WriteStartElement("Node");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Node.Name));
                            xW.WriteAttributeString("TEID", Node.TEID);
                            if (Node.Owner != null)
                                xW.WriteAttributeString("Owner", Node.Owner.TEID);
                            if (Node.Operator != null)
                                xW.WriteAttributeString("Operator", Node.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "Node");
                            xW.WriteAttributeString("KVLevel", GetKV(Node.VoltageLevel));
                            xW.WriteAttributeString("Substation", Node.Substation.TEID);
                            xW.WriteEndElement();

                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:CombinedCyclePlant")
                        {
                            StringBuilder Configurations = new StringBuilder();
                            StringBuilder Units = new StringBuilder();

                            foreach (CIM_Element CCLogical in Elem.Parse("etx:LogicalConfiguration", null))
                                Configurations.Append((Configurations.Length == 0 ? "" : ",") + CCLogical.ParseSingle("etx:Configuration", null).Name);
                            foreach (CIM_Element CCUnit in Elem.Links_Elements)
                                if (CCUnit.TypeName.EndsWith("Unit"))
                                    Units.Append((Units.Length == 0 ? "" : ",") + CCUnit.ParseSingle("cim:SynchronousMachine", null).TEID);

                            xW.WriteStartElement("CombinedCycleConfiguration");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name.Substring(Elem.Substation.Name.Length + 1)));
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            WriteTraces(xW, Elem);
                            xW.WriteAttributeString("ElemType", "CombinedCycleConfiguration");
                            xW.WriteAttributeString("UnitType", "CombinedCycle");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            xW.WriteAttributeString("Units", Units.ToString());
                            xW.WriteAttributeString("Configurations", Configurations.ToString());
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:SynchronousMachine")
                        {
                            CIM_Element Phys = Elem.ParseSingle("Unit", null);

                            xW.WriteStartElement("Unit");
                            String PhysType = Phys.TypeName.Split(':')[1];
                            for (int a = 1; a < PhysType.Length; a++)
                                if (Char.IsUpper(PhysType[a]))
                                {
                                    xW.WriteAttributeString("UnitType", PhysType.Substring(0, a));
                                    break;
                                }
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("UnitTEID", Phys.TEID);
                            if (Elem.Owner == null && Elem.Substation.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Substation.Owner.TEID);
                            else if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "Unit");
                            xW.WriteAttributeString("IsRC", Elem.ParseSingle("Unit>CombinedCyclePlant", null) == null ? "False" : "True");
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            xW.WriteAttributeString("PrimaryFuelType", Elem.PrimaryFuelType);
                            xW.WriteAttributeString("HSL", Phys["etx:GeneratingUnit.highReasonabilityLimit"]);
                            xW.WriteAttributeString("SeasonalMaxSustainableRating", Phys["etx:GeneratingUnit.seasonalMaxSustainableRating"]);
                            xW.WriteAttributeString("MaxCapacity", Phys["etx:GeneratingUnit.maxCapacityLimit"]);

                            //Write out our MVAR capability curve
                            StringBuilder sMVAR = new StringBuilder();
                            foreach (CIM_Element CurveData in Elem.Parse(">cim:MVArCapabilityCurve<cim:CurveData", Model))
                                sMVAR.Append((sMVAR.Length == 0 ? "" : ",") + CurveData["cim:CurveData.xvalue"] + "," + CurveData["cim:CurveData.y1value"] + "," + CurveData["cim:CurveData.y2value"]);
                            xW.WriteAttributeString("MVARCapabilityCurve", sMVAR.ToString());

                            //Check for mothballed status
                            CIM_Element PAN = Phys.ParseSingle(">etx:PrivateAreaNetwork", Model);
                            if (PAN == null)
                                xW.WriteAttributeString("PANType", "None");
                            else
                            {
                                String PanType = PAN["etx:PrivateAreaNetwork.networkType"];
                                xW.WriteAttributeString("PANType", PanType.Substring(PanType.LastIndexOf('.') + 1));
                            }

                            WriteTraces(xW, Elem);
                            //Write out our logical units, if > 1
                            CIM_Element CC = Phys.ParseSingle("cim:CombinedCyclePlant", null);
                            if (CC == null)
                            {
                                CIM_Element[] OwnerShareLimits = Phys.Parse("etx:OwnerShareLimits", null);
                                if (OwnerShareLimits.Length > 1)
                                    foreach (CIM_Element Limit in OwnerShareLimits)
                                    {
                                        xW.WriteStartElement("LogicalUnit");
                                        xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Limit.Name.Substring(Elem.Substation.Name.Length + 1)));
                                        xW.WriteAttributeString("TEID", Limit.TEID);
                                        if (Limit.Owner != null)
                                            xW.WriteAttributeString("Owner", Limit.Owner.TEID);
                                        if (Limit.Operator != null)
                                            xW.WriteAttributeString("Operator", Limit.Operator.TEID);
                                        xW.WriteAttributeString("ElemType", "LogicalUnit");
                                        xW.WriteAttributeString("UnitType", "Logical");
                                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                                        WriteTraces(xW, Elem);
                                        xW.WriteEndElement();
                                    }
                            }
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName.EndsWith("Load"))
                        {
                            bool IsLaar = Elem.ParseSingle("etx:LoadResource", null) != null;
                            xW.WriteStartElement(IsLaar ? "LAAR" : "Load");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (Elem.Owner!=null)
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator!=null)
                            xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", IsLaar ? "LAAR" : "Load");
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            WriteTraces(xW, Elem);
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:Breaker" || Elem.TypeName == "cim:Disconnector" || Elem.TypeName == "cim:GroundDisconnector")
                        {
                            xW.WriteStartElement(Elem.TypeName == "cim:Breaker" ? "Breaker" : "Switch");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator != null)
                                xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", Elem.TypeName == "cim:Breaker" ? "Breaker" : "Switch");
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);

                            CIM_Element Operator = Elem.FindOperator();
                            if (Operator != null)
                                if (Repository.Db.Synchrocheck_Relays.ContainsKey(Elem.rdfID) || Repository.Db.Synchrocheck_Relays.ContainsKey(Elem.Substation.rdfID) || Repository.Db.Synchrocheck_Relays.ContainsKey(Operator.rdfID))
                                    xW.WriteAttributeString("HasSynchrocheckRelay", "true");
                                else
                                    xW.WriteAttributeString("HasSynchrocheckRelay", "false");

                            if (Operator != null)
                                if (Repository.Db.Synchroscopes.ContainsKey(Elem.rdfID) || Repository.Db.Synchroscopes.ContainsKey(Elem.Substation.rdfID) || Repository.Db.Synchroscopes.ContainsKey(Operator.rdfID))
                                    xW.WriteAttributeString("HasSynchroscope", "true");
                                else
                                    xW.WriteAttributeString("HasSynchroscope", "false");
                            xW.WriteAttributeString("NormalOpen", Elem["cim:Switch.normalOpen"]);
                            WriteTraces(xW, Elem);
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:PowerTransformer")
                        {
                            xW.WriteStartElement("Transformer");
                            CIM_Element[] Windings = Elem.TransformerWinding;
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            if (Elem.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            else if (Elem.Substation.Owner != null)
                                xW.WriteAttributeString("Owner", Elem.Substation.Owner.TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (Elem.Operator != null)
                            xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "Transformer");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            xW.WriteAttributeString("Winding1", Windings[0].TEID);
                            xW.WriteAttributeString("KVLevel1", GetKV(Windings[0].VoltageLevel));
                            xW.WriteAttributeString("Winding2", Windings[1].TEID);
                            xW.WriteAttributeString("KVLevel2", GetKV(Windings[1].VoltageLevel));
                            xW.WriteAttributeString("Windings", GetTEIDs(Windings));
                            xW.WriteAttributeString("PhaseShifter", (Elem["cim:PowerTransformer.transformerType"].IndexOf("Phase") != -1 ? "True" : "False"));

                            //Now, produce a list of all voltage windings.
                            List<CIM_Element> Voltages = new List<CIM_Element>();
                            CIM_Element Kluge = Elem.ParseSingle("etx:TransformerKluge", null);
                            if (Kluge == null)
                            {
                                foreach (CIM_Element Winding in Elem.TransformerWinding)
                                    if (!Voltages.Contains(Winding.VoltageLevel))
                                        Voltages.Add(Winding.VoltageLevel);
                            }
                            else
                                foreach (CIM_Element Transformer in Kluge.Parse("cim:PowerTransformer", null))
                                    foreach (CIM_Element Winding in Transformer.TransformerWinding)
                                        if (!Voltages.Contains(Winding.VoltageLevel))
                                            Voltages.Add(Winding.VoltageLevel);

                            //Now, tally up our voltages
                            int VoltageCount = 0;
                            String VoltageName;
                            foreach (CIM_Element Voltage in Voltages)
                                if ((VoltageName = Voltage.Name) == "345 KV" || Voltage.Name == "138 KV" || Voltage.Name == "69 KV")
                                    VoltageCount++;

                            xW.WriteAttributeString("IsAutoTransformer", VoltageCount > 1 ? "true" : "false");

                            WriteTraces(xW, Elem);
                            xW.WriteEndElement();

                            foreach (CIM_Element XFw in Windings)
                            {
                                xW.WriteStartElement("TransformerWinding");
                                xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(XFw.Name));
                                xW.WriteAttributeString("TEID", XFw.TEID);
                                xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                                if (XFw.Owner != null)
                                    xW.WriteAttributeString("Owner", XFw.Owner.TEID);
                                else if (XFw.Substation.Owner != null)
                                    xW.WriteAttributeString("Owner", XFw.Substation.Owner.TEID);
                                if (XFw.Operator!=null)
                                xW.WriteAttributeString("Operator", XFw.Operator.TEID);
                                xW.WriteAttributeString("ElemType", "TransformerWinding");
                                xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                                WriteTraces(xW, Elem);
                                xW.WriteAttributeString("KVLevel", GetKV(XFw.VoltageLevel));
                                xW.WriteAttributeString("Transformer", Elem.TEID);
                                String WindingType = XFw["cim:TransformerWinding.windingType"];
                                xW.WriteAttributeString("WindingType", WindingType.Substring(WindingType.LastIndexOf('.') + 1));
                                xW.WriteAttributeString("WindingNodeTEID", XFw.Nodes[0].TEID);
                                xW.WriteEndElement();
                            }
                        }
                        else if (Elem.TypeName == "cim:StaticVarCompensator")
                        {
                            xW.WriteStartElement("StaticVarCompensator");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (Elem.Owner!=null)
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator!=null)
                            xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "StaticVarCompensator");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            WriteTraces(xW, Elem);
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:ShuntCompensator")
                        {
                            bool IsCapacitor = Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) > 0;
                            xW.WriteStartElement(IsCapacitor ? "Capacitor" : "Reactor");
                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (Elem.Owner!= null)
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                            if (Elem.Operator!=null)
                            xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                            xW.WriteAttributeString("ElemType", IsCapacitor ? "Capacitor" : "Reactor");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            WriteTraces(xW, Elem);
                            xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "etx:DCTie")
                        {
                            xW.WriteStartElement("DCTie");


                            //Retrieve our components
                            CIM_Element Unit = Elem.ParseSingle("Unit", null);
                            CIM_Element Load = Elem.ParseSingle("Load", null);
                            CIM_Element AssociatedLine = Elem.ParseSingle(">TieCorridor<TieLine>Terminal>ACLineSegment", null);


                            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                            xW.WriteAttributeString("TEID", Elem.ParseSingle(">TieCorridor", Repository).TEID);
                            xW.WriteAttributeString("PUNElement", Model.PUNElements.ContainsKey(Elem.rdfID).ToString());
                            if (AssociatedLine.Owner != null)
                                xW.WriteAttributeString("Owner", AssociatedLine.Owner.TEID);
                            if (AssociatedLine.Operator != null)
                                xW.WriteAttributeString("Operator", AssociatedLine.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "DCTie");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            xW.WriteAttributeString("KVLevel", "DCTie");
                            xW.WriteAttributeString("Substation1", AssociatedLine.ACSegmentStations[0].TEID);
                            xW.WriteAttributeString("Substation2", AssociatedLine.ACSegmentStations[1].TEID);
                            if (Unit != null)
                                xW.WriteAttributeString("Unit", Unit.TEID);
                            if (Load != null)
                                xW.WriteAttributeString("Load", Load.TEID);
                            xW.WriteAttributeString("LineTEID", AssociatedLine.ParseSingle("cim:Line", null).TEID);
                            xW.WriteAttributeString("AssociatedLine", AssociatedLine.TEID);
                            xW.WriteEndElement();
                        }
                        else if (Elem.TypeName == "cim:Contingency")
                        {
                            xW.WriteStartElement("Contingency");
                            StringBuilder sB = new StringBuilder();
                            foreach (CIM_Element Sub in Elem.ConnectedStations)
                                sB.Append((sB.Length == 0 ? "" : ",") + Sub.TEID);
                            xW.WriteAttributeString("Name", Elem.Name);
                            xW.WriteAttributeString("Description", (Elem as CIM_Trace).Description);
                            xW.WriteAttributeString("Substations", sB.ToString());
                            xW.WriteEndElement();
                        }
                        /*else if (false && (Elem.TypeName == "etx:SettlementLoadZone" || Elem.TypeName == "cim:SubGeographicalRegion"))
                        {
                            StringBuilder sB = new StringBuilder();
                            foreach (CIM_Element Sub in Elem.Parse("cim:Substation", Repository))
                                if (XmlConvert.ToBoolean(Sub["etx:Substation.forNetwork"]) && XmlConvert.ToBoolean(Sub["etx:Substation.forScada"]))
                                    sB.Append((sB.Length == 0 ? "" : ",") + Sub.TEID.ToString());
                            if (sB.Length > 0)
                            {
                                xW.WriteStartElement("Zone");
                                xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.Name));
                                xW.WriteAttributeString("Substations", sB.ToString());
                                xW.WriteAttributeString("ElemType", Elem.ElemType);
                                xW.WriteEndElement();
                            }
                        }*/
                    }
                    catch (Exception ex)
                    {

                        Console.WriteLine("Error exporting element: " + ex.Message);
                    }


                //Write out our blackstart corridor information
                foreach (MM_Blackstart_Corridor Corridor in Repository.Db.Blackstart_Corridors)
                    try
                    //if (Model.ValidStart >= Corridor.ValidStart && Model.ValidStart <= Corridor.ValidEnd)
                    {
                        xW.WriteStartElement("Blackstart_Corridor");
                        xW.WriteAttributeString("Name", Corridor.Name);
                        xW.WriteAttributeString("Description", Corridor.Description);
                        xW.WriteAttributeString("Notes", Corridor.Notes);

                        //Write out each of our targets
                        foreach (MM_Blackstart_Corridor_Target Target in Corridor.CorridorTargets)
                            try
                            {
                                xW.WriteStartElement("Target");
                                xW.WriteAttributeString("Target", Target.Target);
                                xW.WriteAttributeString("Description", Target.Description);
                                for (int a = 0; a < 2; a++)
                                {
                                    MM_Blackstart_Corridor_Element[] Elems = a == 0 ? Target.Primary : Target.Secondary;
                                    if (Elems != null && Elems.Length > 0)
                                    {
                                        xW.WriteStartElement(a == 0 ? "Primary" : "Secondary");
                                        foreach (MM_Blackstart_Corridor_Element Elem in Elems)
                                        {
                                            Elem.AssignModel(Model);
                                            if (Elem.AssociatedElement != null)
                                                try
                                                {
                                                    xW.WriteStartElement(Elem.AssociatedElement.ElemType);
                                                    xW.WriteAttributeString("Action", Elem.Action.ToString());
                                                    if (Elem.AssociatedElement.Substation != null)
                                                        xW.WriteAttributeString("Substation", Elem.AssociatedElement.Substation.TEID);
                                                    else if (Elem.Substation != null)
                                                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                                                    xW.WriteAttributeString("Name", Elem.AssociatedElement.LineName);
                                                    xW.WriteAttributeString("TEID", Elem.AssociatedElement.TEID);
                                                    if (Elem.AssociatedElement.Owner != null)
                                                    xW.WriteAttributeString("Owner", Elem.AssociatedElement.Owner.TEID);
                                                    if (Elem.AssociatedElement.Operator != null)
                                                        xW.WriteAttributeString("Operator", Elem.AssociatedElement.Operator.TEID);
                                                    xW.WriteAttributeString("ElemType", Elem.AssociatedElement.ElemType);
                                                    xW.WriteAttributeString("KVLevel", GetKV(Elem.AssociatedElement.VoltageLevel));
                                                    xW.WriteEndElement();
                                                }
                                                catch
                                                { }
                                        }
                                        xW.WriteEndElement();
                                    }

                                }

                                xW.WriteEndElement();
                            }
                            catch
                            { }
                        xW.WriteEndElement();
                    }
                    catch
                    { }

                xW.WriteEndElement();
                xW.WriteEndDocument();
                xW.Flush();
            }

            //Write our XML document to the database
            try
            {
                using (DbCommand dCmd = DbConn.CreateCommand("UPDATE MM_DATABASE_MODEL SET MMModel=:0 WHERE ID=:1", File.ReadAllText(TargetFile, new UTF8Encoding(false)), Model.ID))
                    dCmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                ProcessLog.AppendLine("Error uploading MM model file into the database: " + ex.ToString());
            }
        }

        /// <summary>
        /// Locate and write out all traces associated with our element
        /// </summary>
        /// <param name="xW"></param>
        /// <param name="Elem"></param>
        private static void WriteTraces(XmlWriter xW, CIM_Element Elem)
        {
            StringBuilder sB = new StringBuilder(), sB2 = new StringBuilder();
            CIM_Trace[] Traces;
            if (Elem.CIM.TraceLinkages.TryGetValue(Elem.rdfID, out Traces))
                foreach (CIM_Trace OtherTrace in Traces)
                    sB.Append((sB.Length == 0 ? "" : ",") + OtherTrace.Name);
            if (sB.Length > 0)
                xW.WriteAttributeString("Contingencies", sB.ToString());

            foreach (CIM_Element Elem2 in Elem.SPSorRAPs)
                sB2.Append((sB2.Length == 0 ? "" : ",") + Elem2.TEID);
            if (sB2.Length > 0)
                xW.WriteAttributeString("RASs", sB2.ToString());

                
        }
        #endregion

        /// <summary>
        /// Report the KV level of our element
        /// </summary>
        /// <param name="Elem"></param>
        /// <returns></returns>
        private static string GetKV(CIM_Element Elem)
        {
            String VoltageLevel = Elem.TypeName == "cim:BaseVoltage" ? Elem.Name : Elem.ParseSingle("cim:BaseVoltage", null).Name;
            if (VoltageLevel == "345 KV" || VoltageLevel == "230 KV" || VoltageLevel == "138 KV" || VoltageLevel == "69 KV")
                return VoltageLevel;
            else
                return "Other KV";
        }

        /// <summary>
        /// Retrieve a list of TEIDs for an element
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private static string GetTEIDs(IEnumerable<CIM_Element> list)
        {
            StringBuilder OutStr = new StringBuilder();
            foreach (CIM_Element Elem in list)
                OutStr.Append((OutStr.Length == 0 ? "" : ",") + Elem.TEID);
            return OutStr.ToString();
        }

        /// <summary>
        /// Retrieve our list of element types
        /// </summary>
        /// <param name="ElemTally"></param>
        /// <returns></returns>
        private static string GetElemTypes(Dictionary<string, List<CIM_Element>> ElemTally)
        {
            List<String> OutList = new List<string>();
            foreach (KeyValuePair<String, List<CIM_Element>> kvp in ElemTally)
                if (kvp.Key == "ShuntCompensator")
                {
                    foreach (CIM_Element Elem in kvp.Value)
                        if (Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) > 0 && !OutList.Contains("Capacitor"))
                            OutList.Add("Capacitor");
                        else if (Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) < 0 && !OutList.Contains("Reactor"))
                            OutList.Add("Reactor");
                }
                else if (kvp.Key == "Load")
                {
                    foreach (CIM_Element Elem in kvp.Value)
                        if (Elem.ParseSingle("etx:LoadResource", null) != null && !OutList.Contains("LAAR"))
                            OutList.Add("LAAR");
                        else if (Elem.ParseSingle("etx:LoadResource", null) == null && !OutList.Contains("Load"))
                            OutList.Add("Load");
                }
            return string.Join(",", new List<String>(ElemTally.Keys).ToArray());
        }

        /// <summary>
        /// Add an element to our dictionary
        /// </summary>
        /// <param name="ElemTally"></param>
        /// <param name="ElemType"></param>
        /// <param name="Equip"></param>
        private static void AddElement(Dictionary<string, List<CIM_Element>> ElemTally, string ElemType, CIM_Element Equip)
        {
            List<CIM_Element> Elems;
            if (!ElemTally.TryGetValue(ElemType, out Elems))
                ElemTally.Add(ElemType, Elems = new List<CIM_Element>());
            Elems.Add(Equip);
        }

        /// <summary>
        /// Write out a boundary
        /// </summary>
        /// <param name="Boundary"></param>
        /// <param name="xW"></param>
        private static void WriteBoundary(MM_Boundary Boundary, XmlWriter xW)
        {
            xW.WriteStartElement(Boundary.Name.Equals("State", StringComparison.CurrentCultureIgnoreCase) ? "State" : "County");
            xW.WriteAttributeString("Name", Boundary.Name);
            xW.WriteAttributeString("ElemType", Boundary.Name.Equals("State", StringComparison.CurrentCultureIgnoreCase) ? "State" : "County");
            xW.WriteAttributeString("TEID", "0");
            xW.WriteAttributeString("Max", Boundary.Max.X.ToString() + "," + Boundary.Max.Y.ToString());
            xW.WriteAttributeString("Max_X", Boundary.Max.X.ToString());
            xW.WriteAttributeString("Max_Y", Boundary.Max.Y.ToString());
            xW.WriteAttributeString("Min_X", Boundary.Min.X.ToString());
            xW.WriteAttributeString("Min_Y", Boundary.Min.Y.ToString());
            xW.WriteAttributeString("Min", Boundary.Min.X.ToString() + "," + Boundary.Min.Y.ToString());
            xW.WriteAttributeString("Centroid_X", Boundary.Centroid.X.ToString());
            xW.WriteAttributeString("Centroid_Y", Boundary.Centroid.Y.ToString());
            xW.WriteAttributeString("Centroid", Boundary.Centroid.X.ToString() + "," + Boundary.Centroid.Y.ToString());
            StringBuilder sB = new StringBuilder();
            for (int a = 0; a < Boundary.Coordinates.Length; a++)
                sB.Append((a == 0 ? "" : ",") + Boundary.Coordinates[a].X.ToString() + "," + Boundary.Coordinates[a].Y.ToString());
            xW.WriteAttributeString("Coordinates", sB.ToString());
            xW.WriteEndElement();
        }
    }
}